package model

import (
	"fmt"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql" //MySQL 驱动库的初始化（不同类型的 DBType 需要引入不同的驱动库）
	"go-programming-tour-book/blog_service/global"
	otgorm "go-programming-tour-book/blog_service/opentracing-gorm"
	"go-programming-tour-book/blog_service/pkg/setting"
	"time"
)

type Model struct {
	ID         uint32 `json:"id,omitempty" gorm:"primary_key"`
	CreatedBy  string `json:"create_by,omitempty"`
	ModifiedBy string `json:"modified_by,omitempty"`
	CreatedOn  uint32 `json:"created_on,omitempty"`
	ModifiedOn uint32 `json:"modified_on,omitempty"`
	DeleteOn   uint32 `json:"delete_on,omitempty"`
	IsDel      uint8  `json:"is_del,omitempty"`
}

func NewDBEngine(databaseSettings *setting.DatabaseSettings) (*gorm.DB, error) {
	s := "%s:%s@tcp(%s)/%s?charset=%s&parseTime=%t&loc=Local"
	db, err := gorm.Open(databaseSettings.DBType, fmt.Sprintf(s,
		databaseSettings.UserName,
		databaseSettings.Password,
		databaseSettings.Host,
		databaseSettings.DBName,
		databaseSettings.Charset,
		databaseSettings.ParseTime))
	if err != nil {
		return nil, err
	}
	if global.ServerSetting.RunMode == "debug" {
		db.LogMode(true)
	}

	//修改 gorm 的命名策略
	gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
		return databaseSettings.TablePrefix + defaultTableName
	}

	//注册回调行为
	db.SingularTable(true)
	db.Callback().Create().Replace("gorm:update_time_stamp", updateTimeStampForCreateCallback)
	db.Callback().Update().Replace("gorm:update_time_stamp", updateTimeStampForUpdateCallback)
	db.Callback().Delete().Replace("gorm:delete", deleteCallback)

	db.DB().SetMaxIdleConns(databaseSettings.MaxIdleConns)
	db.DB().SetMaxOpenConns(databaseSettings.MaxOpenConns)

	otgorm.AddGormCallbacks(db)

	return db, nil
}

// 新增行为的回调
func updateTimeStampForCreateCallback(scope *gorm.Scope) {
	if !scope.HasError() {
		nowTime := time.Now().Unix()
		//调用 FieldByName 方法，获取当前是否包含所需的字段
		if createTimeField, ok := scope.FieldByName("CreateOn"); ok {
			if createTimeField.IsBlank { //判断该字段的值是否为空
				//如果为空，调用 Field.Set 函数给该字段设置值、入参类型为 interface{}，即内部是通过反射进行一系列操作赋值的。
				_ = createTimeField.Set(nowTime)
			}
		}

		if modifyTimeField, ok := scope.FieldByName("ModifiedOn"); ok {
			if modifyTimeField.IsBlank {
				_ = modifyTimeField.Set(nowTime)
			}
		}
	}
}

// 更新行为的回调
func updateTimeStampForUpdateCallback(scope *gorm.Scope) {
	//通过调用 scope.Get 来获取当前设置的标识 gorm:update_column 的字段属性
	if _, ok := scope.Get("gorm:update_column"); !ok {
		//若不存在，即没有自定义设置 update_column，则在更新回调内设置默认字段 ModifiedOn 的值为当前的时间戳
		_ = scope.SetColumn("ModifiedOn", time.Now().Unix())
	}
}

// 删除行为的回调
func deleteCallback(scope *gorm.Scope) {
	if !scope.HasError() {
		var extraOption string
		//通过调用 scope.Get 来获取当前设置的标识 gorm:delete_option 的字段属性
		if str, ok := scope.Get("gorm:delete_option"); ok {
			extraOption = fmt.Sprint(str)
		}

		//判断是否存在 DeletedOn 和 IsDel 字段。
		deletedOnField, hasDeletedOnField := scope.FieldByName("DeletedOn")
		isDelField, hasIsDelField := scope.FieldByName("IsDel")
		//若存在，则执行 UPDATE 操作进行软删除（修改DeletedOn 和 IsDel 的值）
		if !scope.Search.Unscoped && hasDeletedOnField && hasIsDelField {
			now := time.Now().Unix()
			scope.Raw(fmt.Sprintf(
				"UPDATE %v SET %v=%v%v%v",
				scope.QuotedTableName(), //获取当前引用的表名
				scope.Quote(deletedOnField.DBName),
				scope.AddToVars(now),
				scope.Quote(isDelField.DBName),
				scope.AddToVars(1),
				addExtraSpaceIfExist(scope.CombinedConditionSql()), //在完成一些所需参数设置后，调用 scope.CombinedConditionSql 方法完成 SQL 语句的组装
				addExtraSpaceIfExist(extraOption),
			)).Exec()
		} else { //否则执行 DELETE 操作进行硬删除
			scope.Raw(fmt.Sprintf(
				"DELETE FROM %v%v%v",
				scope.QuotedTableName(),                            //获取当前引用的表名
				addExtraSpaceIfExist(scope.CombinedConditionSql()), //在完成一些所需参数设置后，调用 scope.CombinedConditionSql 方法完成 SQL 语句的组装
				addExtraSpaceIfExist(extraOption),
			)).Exec()
		}
	}
}

func addExtraSpaceIfExist(str string) string {
	if str != "" {
		return " " + str
	}
	return ""
}
